package com.retail.lottery.colalottery.job.notice.service;

import cn.hutool.json.JSONUtil;
import com.alibaba.excel.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.retail.lottery.colalottery.constant.RedisConstant;
import com.retail.lottery.colalottery.entity.*;
import com.retail.lottery.colalottery.enums.*;
import com.retail.lottery.colalottery.job.node.RedisNodeIpManager;
import com.retail.lottery.colalottery.service.*;
import com.retail.lottery.colalottery.service.cola.ColaClientService;
import com.retail.lottery.colalottery.utils.RedisUtils;
import com.retail.lottery.colalottery.vo.MessageNoticeVo;
import com.retail.lottery.colalottery.vo.NodeInfoVo;
import com.retail.lottery.colalottery.vo.request.GetLotteryTimesRequest;
import com.retail.lottery.colalottery.vo.request.SendMessageNoticeRequest;
import com.retail.lottery.colalottery.vo.response.GetLotteryTimesResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Service
@Slf4j
public class LotteryMessageNoticeCommonService extends RedisNodeIpManager {

    @Value("${lottery-threshold}")
    private String lotteryThreshold;
    // 美团抽奖活动配置
    @Value("${api-client-campaignCode}")
    private String lotteryCampaignCode;
    @Value("${api-client-buCode}")
    private String lotteryBuCode;
    @Value("${api-client-setupId}")
    private String lotterySetupId;
    // 烧烤节活动配置
    @Value("${api-bbq-client-campaignCode}")
    private String bbqCampaignCode;
    @Value("${api-bbq-client-buCode}")
    private String bbqBuCode;
    @Value("${api-bbq-client-setupId}")
    private String bbqSetupId;
    @Resource
    private MessageNoticeRecordService messageNoticeRecordService;
    @Resource
    private NoticeTemplateService noticeTemplateService;
    @Resource
    private WechatCustomerService wechatCustomerService;
    @Resource
    private ColaClientService colaClientService;
    @Resource
    private OrderCallbackService orderCallbackService;
    @Resource
    private OrderDetailService orderDetailService;

    /**
     * 创建抽奖消息通知的主入口方法
     * 
     * 核心逻辑：
     * 1. 使用分布式锁防止重复执行
     * 2. 获取当前节点的分片信息
     * 3. 查询待处理的订单回调记录
     * 4. 根据分片策略筛选当前节点需要处理的数据
     * 5. 为每个符合条件的订单创建消息通知
     * 
     * 分片策略：
     * 通过encryptMobile的hash值对分片总数取模，确保同一用户的数据总是由同一节点处理
     */
    public void createNotice() {
        // 获取当前节点信息（用于分布式环境下的分片处理）
        NodeInfoVo nodeInfoVo = localNodeInfo();
        int shardingTotalCount = nodeInfoVo.getNodeNum();   // 总分片数
        int shardingItem = nodeInfoVo.getCurrentNode();     // 当前分片编号
        
        // 构造Redis分布式锁的key
        String redisKey = String.format(RedisConstant.REDIS_KEY_MESSAGE_NOTICE_CREATE, shardingTotalCount, shardingItem);
        
        // 尝试获取分布式锁，防止多个节点同时处理相同分片的数据
        if (!RedisUtils.saveNX(redisKey, "1", 30, TimeUnit.SECONDS)) {
            log.info("创建订阅消息通知记录Job，任务总片数: {}, 当前分片项: {}, 数据处理正在执行中，未获取锁", shardingTotalCount, shardingItem);
            return;
        }

        try {
            // 查询待处理的订单回调记录（状态为同步成功且未处理消息标记）
            List<OrderCallback> callbacks = orderCallbackService.getBaseMapper().selectList(new LambdaQueryWrapper<OrderCallback>()
                    .eq(OrderCallback::getSyncStatus, OrderCallbackSyncStatusEnum.SYNC_SUCCESS.getStatus())
                    .eq(OrderCallback::getMessageFlag, DeleteStatusEnum.no_del.getCode())
                    .last(" order by id asc limit 100"));
            
            // 如果没有待处理数据，直接返回
            if (CollectionUtils.isEmpty(callbacks)) {
                log.info("创建订阅消息通知记录Job，任务总片数: {}, 当前分片项: {}, 当前无待处理数据", shardingTotalCount, shardingItem);
                return;
            }
            
            // 根据分片策略筛选当前节点需要处理的数据
            // 使用encryptMobile的hash值对分片总数取模，确保同一用户的数据总是由同一节点处理
            List<OrderCallback> groupByHash = callbacks.stream().filter(callback -> {
                int dataShardingItem = callback.getEncryptMobile().hashCode() % shardingTotalCount;
                dataShardingItem = dataShardingItem < 0 ? (dataShardingItem * -1) : dataShardingItem;  // 处理负数情况
                return dataShardingItem == shardingItem;
            }).collect(Collectors.toList());
            
            // 如果当前分片无待处理数据，直接返回
            if (CollectionUtils.isEmpty(groupByHash)) {
                log.info("创建订阅消息通知记录Job，任务总片数: {}, 当前分片项: {}, 当前分片无待处理数据", shardingTotalCount, shardingItem);
                return;
            }
            
            // 将阈值字符串按逗号分割为列表
            List<String> thresholdLevels = Arrays.asList(lotteryThreshold.split(","));
            
            // 为每个符合条件的订单创建消息通知
            for (OrderCallback callback : groupByHash) {
                createMessageInfo(callback, thresholdLevels, shardingTotalCount, shardingItem);
            }
        } catch (Exception e) {
            log.error("创建订阅消息通知记录Job，任务总片数: {}, 当前分片项: {}, 创建通知消息Job异常", shardingTotalCount, shardingItem, e);
        } finally {
            // 释放分布式锁
            RedisUtils.del(redisKey);
        }
    }

    /**
     * 为满足条件的用户创建抽奖消息通知记录
     * 
     * 核心逻辑：
     * 1. 计算用户累计消费金额
     * 2. 判断用户当前达到的消费档位
     * 3. 与历史档位比较，避免重复通知
     * 4. 为新达到的档位创建抽奖通知消息
     * 5. 必要时创建召回通知
     * 
     * @param callback 当前处理的订单回调记录
     * @param thresholdLevels 预设的消费金额阈值档位列表
     * @param shardingTotalCount 分片总数量
     * @param shardingItem 当前分片编号
     */
    private void createMessageInfo(OrderCallback callback, List<String> thresholdLevels, int shardingTotalCount, int shardingItem) {
        try {
            log.info("创建订阅消息通知记录Job，任务总片数: {}, 当前分片项: {}, 开始创建通知消息：{}", shardingTotalCount, shardingItem, callback.getId());
            
            // 查询当前用户（通过encryptMobile识别）的所有订单回调记录（ID小于等于当前订单ID）
            Set<Integer> orderCallbackIds = orderCallbackService.getBaseMapper().selectList(new LambdaQueryWrapper<OrderCallback>()
                            .eq(OrderCallback::getEncryptMobile, callback.getEncryptMobile())
                            .le(OrderCallback::getId, callback.getId())).stream()
                    .map(OrderCallback::getId)
                    .collect(Collectors.toSet());

            // 根据订单回调ID查询对应的订单详情
            List<OrderDetail> orderDetails = orderDetailService.getBaseMapper().selectList(new LambdaQueryWrapper<OrderDetail>()
                    .in(OrderDetail::getCallbackId, orderCallbackIds));
            
            // 计算用户累计下单总金额
            BigDecimal amount = orderDetails.stream()
                    .map(OrderDetail::getTotalActualPrice)
                    .map(price -> price.replaceAll("[^\\d.]", ""))  // 过滤掉非数字字符
                    .map(BigDecimal::new)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            
            // 1. 判断当前人满足的档位
            // 获取当前用户所有消息通知记录
            List<MessageNoticeVo> templateInfo = messageNoticeRecordService.getTemplateInfo(callback.getWechatCustomerId());
            
            // 筛选出抽奖类型的通知记录
            List<MessageNoticeVo> lotteryMessageNotices = templateInfo.stream()
                    .filter(x -> Objects.equals(x.getNoticeType(), NoticeTypeEnum.LOTTERY.getType()))
                    .collect(Collectors.toList());
            
            // 计算用户历史已达到的最高档位，如果没有则为-1
            int historyThresholdLevel = CollectionUtils.isEmpty(lotteryMessageNotices) ? -1 : lotteryMessageNotices.stream()
                    .map(MessageNoticeVo::getThresholdLevel)
                    .max(Integer::compareTo)
                    .get();
            
            // 计算当前金额能达到的最高档位
            int largestValueLessThan = findLargestValueLessThan(thresholdLevels, amount);
            
            // 如果金额超过最大阈值，每增加10元增加一个新档位
            if (largestValueLessThan >= thresholdLevels.size() - 1 && !thresholdLevels.isEmpty()) {
                // 获取最大阈值
                BigDecimal maxThreshold = new BigDecimal(thresholdLevels.get(thresholdLevels.size() - 1));
                // 如果当前金额超过最大阈值
                if (amount.compareTo(maxThreshold) > 0) {
                    // 计算超出部分可以兑换的额外档位数
                    BigDecimal excessAmount = amount.subtract(maxThreshold);
                    int extraLevels = excessAmount.divide(new BigDecimal("10"), 0, BigDecimal.ROUND_DOWN).intValue();
                    
                    // 最多追加1000个档位
                    extraLevels = Math.min(extraLevels, 1000);
                    
                    // 更新档位索引（基础阈值索引 + 额外档位数）
                    largestValueLessThan = (thresholdLevels.size() - 1) + extraLevels;
                }
            }
            
            log.info("创建订阅消息通知记录Job，任务总片数: {}, 当前分片项: {}, 订单:{}, 当前金额：{}, 当前达成档位：{}, 历史达成档位：{}, ", 
                    shardingTotalCount, shardingItem, callback.getId(), amount, largestValueLessThan, historyThresholdLevel);
            
            // 如果当前档位与历史档位相同，说明用户未达到新档位，无需创建新通知
            if (largestValueLessThan == historyThresholdLevel) {
                log.info("创建通知消息Job，当前用户当前金额未到达下一个档位，订单ID:{}，金额：{},档位：{}", callback.getId(), amount, largestValueLessThan);
                callback.setMessageFlag(DeleteStatusEnum.del.getCode());
                orderCallbackService.updateById(callback);
                return;
            }
            
            // 获取当前时间，用于设置消息发送时间
            LocalDateTime now = LocalDateTime.now();
            
            // 2. 创建抽奖消息通知记录
            MessageNoticeRecord lotteryMessageNotice = new MessageNoticeRecord();
            lotteryMessageNotice.setBuCode(lotteryBuCode);              // 业务代码
            lotteryMessageNotice.setSetupId(lotterySetupId);            // 设置ID
            lotteryMessageNotice.setEncryptMobile(callback.getEncryptMobile());     // 用户加密手机号
            lotteryMessageNotice.setWechatCustomerId(callback.getWechatCustomerId()); // 微信客户ID
            lotteryMessageNotice.setThresholdOrderId(callback.getId());             // 阈值订单ID
            lotteryMessageNotice.setThresholdValue(amount.longValue() * 100);       // 阈值金额（单位：分）
            lotteryMessageNotice.setThresholdLevel(largestValueLessThan);           // 阈值档位
            lotteryMessageNotice.setLotteryNum(largestValueLessThan - historyThresholdLevel);  // 抽奖次数
            
            // 查询对应的通知模板
            NoticeTemplate noticeTemplateId = noticeTemplateService.getNoticeTemplateId(lotteryBuCode, lotterySetupId, NoticeTypeEnum.LOTTERY.getType());
            lotteryMessageNotice.setNoticeTemplateId(noticeTemplateId.getId());     // 模板ID
            lotteryMessageNotice.setNoticeStatus(MessageNoticeStatusEnum.WAIT.getStatus());  // 设置通知状态为待发送
            
            // 设置通知发送时间为当前时间加10分钟
            LocalDateTime newTime = now.plusMinutes(10);
            Date newDate = Date.from(newTime.atZone(ZoneId.systemDefault()).toInstant());
            lotteryMessageNotice.setNoticeDate(newDate);

            // 3. 判断是否需要创建召回通知
            // 如果用户没有已发送成功的召回通知，则创建召回通知
            if (CollectionUtils.isEmpty(templateInfo.stream()
                    .filter(x -> x.getNoticeType() == NoticeTypeEnum.RECALL.getType()
                            && x.getNoticeStatus() == MessageNoticeStatusEnum.SUCCESS.getStatus())
                    .collect(Collectors.toList()))) {
                log.info("创建通知消息Job，当前无已发送的召回信息，订单ID:{}，执行创建召回逻辑", callback.getId());
                messageNoticeRecordService.createRecallNotice(lotteryBuCode, lotterySetupId, callback);
            }

            // 4. 保存抽奖消息通知记录
            if (messageNoticeRecordService.save(lotteryMessageNotice)) {
                // 保存成功后更新订单回调记录状态
                callback.setMessageFlag(DeleteStatusEnum.del.getCode());
                orderCallbackService.updateById(callback);
            }

        } catch (Exception e) {
            log.error("创建通知消息Job异常，订单ID:{}", callback.getId(), e);
        }
    }


    /**
     * 发送消息通知的主入口方法
     * 
     * 核心逻辑：
     * 1. 使用分布式锁防止重复执行
     * 2. 获取当前节点的分片信息
     * 3. 查询待发送的通知记录（待发送或重新发送状态且发送时间已到）
     * 4. 根据分片策略筛选当前节点需要处理的数据
     * 5. 逐个发送通知消息
     * 
     * 分片策略：
     * 通过记录ID对分片总数取模，确保同一记录总是由同一节点处理
     */
    public void sendNotice() {
        // 获取当前节点信息（用于分布式环境下的分片处理）
        NodeInfoVo nodeInfoVo = localNodeInfo();
        int shardingTotalCount = nodeInfoVo.getNodeNum();   // 总分片数
        int shardingItem = nodeInfoVo.getCurrentNode();     // 当前分片编号
        
        log.info("发送订阅消息通知Job，任务总片数: {}, 当前分片项: {}", shardingTotalCount, shardingItem);
        
        // 构造Redis分布式锁的key
        String redisKey = String.format(RedisConstant.REDIS_KEY_MESSAGE_NOTICE_SEND, shardingTotalCount, shardingItem);
        
        // 尝试获取分布式锁，防止多个节点同时处理相同分片的数据
        if (!RedisUtils.saveNX(redisKey, "1", 30, TimeUnit.SECONDS)) {
            log.info("发送订阅消息通知Job，任务总片数: {}, 当前分片项: {}，发送数据处理正在执行中，未获取锁", shardingTotalCount, shardingItem);
            return;
        }

        try {
            // 查询待发送的通知记录（待发送或重新发送状态且发送时间已到）
            // 同时根据分片策略筛选当前节点需要处理的数据（通过ID取模）
            List<MessageNoticeRecord> records = messageNoticeRecordService.getBaseMapper().selectList(new LambdaQueryWrapper<MessageNoticeRecord>()
                    .in(MessageNoticeRecord::getNoticeStatus, Arrays.asList(MessageNoticeStatusEnum.WAIT.getStatus(), MessageNoticeStatusEnum.AGAIN.getStatus()))
                    .lt(MessageNoticeRecord::getNoticeDate, LocalDateTime.now())
                    .last(" and id % " + shardingTotalCount + "= " + shardingItem + " order by id asc limit 100"));
            
            // 如果没有待处理数据，直接返回
            if (CollectionUtils.isEmpty(records)) {
                log.info("发送订阅消息通知Job，任务总片数: {}, 当前分片项: {}，当前无待处理数据", shardingTotalCount, shardingItem);
                return;
            }

            // 逐个发送通知消息
            for (MessageNoticeRecord record : records) {
                // 查询通知模板
                NoticeTemplate noticeTemplate = noticeTemplateService.getBaseMapper().selectById(record.getNoticeTemplateId());
                // 发送通知
                sendRecord(record, noticeTemplate, shardingTotalCount, shardingItem);
            }
        } catch (Exception e) {
            log.error("发送订阅消息通知Job，任务总片数: {}, 当前分片项: {}，订阅信息发送JOB异常，锁:{}", shardingTotalCount, shardingItem, redisKey, e);
        } finally {
            // 释放分布式锁
            RedisUtils.del(redisKey);
        }
        log.info("发送订阅消息通知Job，任务总片数: {}, 当前分片项: {}，执行结束", shardingTotalCount, shardingItem);
    }

    /**
     * 发送单条消息通知记录
     * 
     * 核心逻辑：
     * 1. 根据通知类型进行不同处理：
     *    - 抽奖通知：直接发送
     *    - 召回通知：检查用户是否已发送过召回通知，并查询用户是否有抽奖次数
     * 2. 组装发送参数并调用可乐接口发送消息
     * 3. 根据发送结果更新通知记录状态
     * 
     * @param record 待发送的通知记录
     * @param noticeTemplate 通知模板
     * @param shardingTotalCount 分片总数量
     * @param shardingItem 当前分片编号
     */
    private void sendRecord(MessageNoticeRecord record, NoticeTemplate noticeTemplate, int shardingTotalCount, int shardingItem) {
        // 1. 抽奖消息通知逻辑，无需额外判断，直接发送即可
        // 2. 召回消息通知逻辑，判断此活动下此用户是否已经发送过召回信息，已发送过则不需要再次发送
        try {
            // 检查通知模板是否存在
            if (Objects.isNull(noticeTemplate)) {
                log.error("发送订阅消息通知Job，任务总片数: {}, 当前分片项: {}，发送消息通知失败，模板不存在，记录ID:{}", shardingTotalCount, shardingItem, record.getId());
                throw new RuntimeException("发送消息通知失败，模板不存在");
            }
            
            // 查询微信用户信息
            WechatCustomer wechatCustomer = wechatCustomerService.getBaseMapper().selectById(record.getWechatCustomerId());
            if (Objects.isNull(wechatCustomer)) {
                log.error("发送订阅消息通知Job，任务总片数: {}, 当前分片项: {}，发送消息通知失败，用户不存在，记录ID:{}", shardingTotalCount, shardingItem, record.getId());
                throw new RuntimeException("发送消息通知失败，用户不存在");
            }

            // 如果是召回通知，需要进行额外判断
            if (NoticeTypeEnum.RECALL.getType().equals(noticeTemplate.getNoticeType())) {
                // 检查此活动下此用户是否已经发送过召回信息
                MessageNoticeRecord hasRecallMessage = messageNoticeRecordService.getBaseMapper().selectOne(new LambdaQueryWrapper<MessageNoticeRecord>()
                        .eq(MessageNoticeRecord::getBuCode, record.getBuCode())
                        .eq(MessageNoticeRecord::getSetupId, record.getSetupId())
                        .eq(MessageNoticeRecord::getNoticeTemplateId, noticeTemplate.getId())
                        .eq(MessageNoticeRecord::getWechatCustomerId, record.getWechatCustomerId())
                        .eq(MessageNoticeRecord::getNoticeStatus, MessageNoticeStatusEnum.SUCCESS.getStatus())
                        .last("limit 1"));
                
                // 如果已发送过召回信息，则不再发送
                if (Objects.nonNull(hasRecallMessage)) {
                    log.info("发送订阅消息通知Job，任务总片数: {}, 当前分片项: {}，发送消息通知结束，此用户已发送过召回信息，记录ID:{}", shardingTotalCount, shardingItem, record.getId());
                    record.setNoticeStatus(MessageNoticeStatusEnum.NO_REISSUE.getStatus());
                    record.setNoticeDate(new Date());
                    messageNoticeRecordService.updateById(record);
                    return;
                }
                
                // 查询用户是否有剩余抽奖次数
                GetLotteryTimesRequest request = new GetLotteryTimesRequest();
                request.setSetupId(noticeTemplate.getSetupId());
                request.setCustomerId(wechatCustomer.getThirdCustomerId());
                request.setOpenId(wechatCustomer.getOpenId());
                GetLotteryTimesResponse getLotteryTimesResponse = colaClientService.queryLotteryTimesInfo(record.getBuCode(), request);
                
                // 如果查询抽奖次数接口异常，暂不发送
                if (Objects.isNull(getLotteryTimesResponse)) {
                    log.info("发送订阅消息通知Job，任务总片数: {}, 当前分片项: {}，获取客户抽奖次数接口异常，不确认是否有抽奖次数，暂不发送，记录ID:{}", shardingTotalCount, shardingItem, record.getId());
                    throw new RuntimeException("发送消息通知失败，查询可乐抽奖次数接口失败");
                }
                
                // 如果用户没有剩余抽奖次数，则不发送召回通知
                if (Objects.nonNull(getLotteryTimesResponse)
                        && Objects.nonNull(getLotteryTimesResponse.getData())
                        && getLotteryTimesResponse.getData().getTotalLeftTimes() <= 0) {
                    log.info("发送订阅消息通知Job，任务总片数: {}, 当前分片项: {}，发送消息通知结束，此用户没有剩余抽奖次数，记录ID:{}", shardingTotalCount, shardingItem, record.getId());
                    record.setNoticeStatus(MessageNoticeStatusEnum.NO_REISSUE.getStatus());
                    record.setNoticeDate(new Date());
                    messageNoticeRecordService.updateById(record);
                    return;
                }
                
                log.info("发送订阅消息通知Job，任务总片数: {}, 当前分片项: {}，发送执行结束，记录ID:{}", shardingTotalCount, shardingItem, record.getId());
            }

            // 2. 组装发送参数
            SendMessageNoticeRequest request = new SendMessageNoticeRequest();
            // 根据设置ID确定活动编码
            if(lotterySetupId.equals(noticeTemplate.getSetupId())){
                request.setCampaignCode(lotteryCampaignCode);
            }else if(bbqSetupId.equals(noticeTemplate.getSetupId())){
                request.setCampaignCode(bbqCampaignCode);
            }
            request.setType("CAMPAIGN");                             // 消息类型
            request.setToUser(wechatCustomer.getOpenId());           // 接收用户openid
            request.setTemplateId(noticeTemplate.getTemplateId());   // 模板ID
            
            // 设置模板属性数据
            if (StringUtils.isNotBlank(noticeTemplate.getTemplateProperty())) {
                Map<String, Object> map = JSONUtil.parseObj(noticeTemplate.getTemplateProperty()).toBean(Map.class);
                request.setData(map);
            }
            request.setPage(noticeTemplate.getTemplatePage());       // 跳转页面

            // 3. 调用可乐接口发送消息
            Boolean responseResult = colaClientService.sendMessageNotice(record.getBuCode(), noticeTemplate.getSetupId(), request);
            log.info("发送订阅消息通知Job，任务总片数: {}, 当前分片项: {}，记录ID:{}，发送消息通知结果:{}", shardingTotalCount, shardingItem, record.getId(), responseResult);
            
            // 4. 根据发送结果更新通知记录状态
            // 调用接口成功也不意味着客户就一定能收到通知
            if (responseResult) {
                record.setNoticeStatus(MessageNoticeStatusEnum.SUCCESS.getStatus());  // 发送成功
                record.setNoticeDate(new Date());
                record.setNoticeNum(record.getNoticeNum() + 1);  // 发送次数加1
            } else {
                throw new RuntimeException("发送消息通知失败，请求可乐接口失败");
            }
        } catch (Exception e) {
            log.error("发送订阅消息通知Job，任务总片数: {}, 当前分片项: {}，记录ID:{}，发送消息通知异常:{}", shardingTotalCount, shardingItem, record.getId(), e);
            // 发送失败时更新通知次数和状态
            record.setNoticeNum(record.getNoticeNum() + 1);
            // 如果发送次数达到3次，则标记为失败状态，否则标记为重新发送状态
            record.setNoticeStatus(record.getNoticeNum() >= 3 ? MessageNoticeStatusEnum.FAIL.getStatus() : MessageNoticeStatusEnum.AGAIN.getStatus());
        }
        // 更新通知记录状态
        messageNoticeRecordService.updateById(record);
    }


    /**
     * 查找小于指定金额的最大阈值档位索引
     * 
     * 算法逻辑：
     * 1. 将字符串类型的阈值列表转换为BigDecimal类型
     * 2. 遍历所有阈值，找出小于指定金额的阈值
     * 3. 在这些阈值中找出最大的一个，并返回其索引
     * 
     * 示例：
     * 阈值列表: ["3", "9", "24", "48"]
     * 历史金额: 15
     * 结果: 1 (对应阈值9，因为只有3和9小于15，其中9最大)
     * 
     * @param stringNumbers 字符串类型的阈值列表
     * @param historyAmount 历史消费金额
     * @return 小于指定金额的最大阈值在列表中的索引，如果没有则返回-1
     */
    public static int findLargestValueLessThan(List<String> stringNumbers, BigDecimal historyAmount) {
        // 将字符串类型的阈值列表转换为BigDecimal类型
        List<BigDecimal> numbers = stringNumbers.stream()
                .map(BigDecimal::new)
                .collect(Collectors.toList());

        // 使用IntStream遍历阈值列表，筛选出小于指定金额的阈值索引，
        // 然后在这些索引中找出对应金额最大的那个索引
        return IntStream.range(0, numbers.size())
                .filter(i -> numbers.get(i).compareTo(historyAmount) < 0)  // 筛选小于指定金额的阈值
                .reduce((a, b) -> numbers.get(a).compareTo(numbers.get(b)) >= 0 ? a : b)  // 找出金额最大的阈值索引
                .orElse(-1);  // 如果没有符合条件的阈值，返回-1
    }

//    public static void main(String[] args){
//        List<String> stringNumbers = Arrays.asList("10", "20", "30", "40", "50", "60", "70", "80", "90", "100");
//        System.out.println(findLargestValueLessThan(stringNumbers, new BigDecimal("13")));
//    }
}
